#!/usr/bin/env python2
#
# buildCentralAgent
#
# Copyright (C) 2010 Antoine Mercadal <antoine.mercadal@inframonde.eu>
# Copyright (C) 2013 Nicolas Ochem <nicolas.ochem@free.fr>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import sys, os
import argparse

PATH = os.path.dirname(os.path.realpath(__file__))
LOG_PATH = os.path.join(PATH, "buildCentralAgent.log")

### Log messages

def success(msg):
    """
    Print a standardized success message
    @type msg: String
    @param msg: the message to print
    """
    print "\033[32mSUCCESS: %s\033[0m" % msg

def error(msg, exit=True):
    """
    Print a standardized success message
    @type msg: String
    @param msg: the message to print
    @type exit: Boolean
    @param exit: if True, exit after print
    """
    print "\033[31mERROR: %s\033[0m" % msg
    if exit:
        sys.exit(1)

def msg(msg, exit=True):
    """
    Print a standardized neutral message
    @type msg: String
    @param msg: the message to print
    @type exit: Boolean
    @param exit: if True, exit after print
    """
    print "\033[35mMESSAGE: %s\033[0m" % msg

def warn(msg):
    """
    Print a standardized warning message
    @type msg: String
    @param msg: the message to print
    """
    print "\033[33mWARNING: %s\033[0m" % msg

### Repo parsing

def parse_repo(path):
    """
    Parse the repository and return an array of all archipel modules
    @type path: string
    @param path: the base path
    @rtype: list
    @return: array of full paths of all modules
    """
    paths = []
    for plugin_folder in os.listdir(path):
        if os.path.isdir(plugin_folder) and (plugin_folder.startswith("archipel-central-agent") or plugin_folder.startswith("archipel-core") or plugin_folder.startswith("archipel-platformrequest") or plugin_folder.startswith("archipel-agent-xmppserver")):
            paths.append(os.path.join(path, plugin_folder))
    return paths


### Build functions

def pypi_register(package_paths):
    """
    register all package against pypi.
    You need the file ~/.pypirc containing your information.
    For instance:
    ```ini
        [pypirc]
        servers =
            pypi
        [pypi]
        username:username
        password:yourpassword
        [server-login]
        username:username
        password:yourpassword
    ```
    @type package_paths: list
    @param package_paths: array of all module paths
    """
    for package_path in package_paths:
        os.chdir(package_path)
        if os.system("python setup.py register") == 0:
            success("Package %s registered" % os.path.basename(package_path))
        else:
            error("Unable to register package %s" % os.path.basename(package_path))
        os.chdir("..")

def install_dev(package_paths):
    """
    Perform the dev installation (symlink install)
    @type package_paths: list
    @param package_paths: array of all module paths
    """
    for package_path in package_paths:
        os.chdir(package_path)
        if os.system("python setup.py develop --no-deps &>> %s" % LOG_PATH) == 0:
            success("EGG package %s installed in developer mode" % os.path.basename(package_path))
        else:
            error("Unable to install EGG package %s in developer mode" % os.path.basename(package_path))
        os.chdir("..")

def install_clean(repo_path):
    """
    Clean all the build artifacts
    @type repo_path: String
    @param repo_path: base path of the packages
    """
    os.system('find %s -maxdepth 2 -name "*.egg-info" -type d -exec rm -rf "{}" \;' % repo_path)
    os.system('find %s -maxdepth 2 -name "build" -type d -exec rm -rf "{}" \;' % repo_path)
    os.system('find %s -maxdepth 2 -name "dist" -type d -exec rm -rf "{}" \;' % repo_path)
    os.system('find %s -maxdepth 2 -name "temp" -type d -exec rm -rf "{}" \;' % repo_path)
    os.system("rm -rf Archipel_EGGS Archipel_RPMS buildAgent.log")
    success("build cleaned")

def generate_eggs(package_paths, upload=False):
    """
    Generate python EGGS
    @type package_paths: list
    @param package_paths: array of all module paths
    @type upload: Boolean
    @param upload: if True, will upload the eggs to pypi. (Default: False)
    """
    for package_path in package_paths:
        os.chdir(package_path)
        if upload:
            if os.system("python setup.py bdist_egg upload &>> %s" % LOG_PATH) == 0:
                success("EGG package %s generated and uploaded" % os.path.basename(package_path))
            else:
                error("Unable to build and/or upload EGG package %s" % os.path.basename(package_path))
        else:
            if os.system("python setup.py bdist_egg &>> %s" % LOG_PATH) == 0:
                success("EGG package %s generated" % os.path.basename(package_path))
            else:
                error("Unable to build EGG package %s" % os.path.basename(package_path))
        os.chdir("..")

def generate_rpms(package_paths):
    """
    Generate RPMS
    @type package_paths: list
    @param package_paths: array of all module paths
    """
    for package_path in package_paths:
        os.chdir(package_path)
        if os.system("python setup.py bdist_rpm &>> %s" % LOG_PATH) == 0:
            success("RPM package %s generated" % os.path.basename(package_path))
        else:
            error("Unable to build RPM package %s" % os.path.basename(package_path))
        os.chdir("..")

def export_eggs(repo_path, package_paths, export_path=None):
    """
    Export all generated EGGS in custom folder
    @type repo_path: String
    @param repo_path: base path of the packages
    @type package_path: list
    @param package_path: array of all module paths
    @type export_path: string
    @param export_path: the destination path. if None, it will be exported in ./Archipel_EGGS
    """
    if not export_path:
        export_path = os.path.join(repo_path, "Archipel_EGGS")
    if not os.path.isdir(export_path):
        os.makedirs(export_path)
    for package_path in package_paths:
        if os.system("cp -a %s/dist/*.egg %s" % (package_path, export_path)) == 0:
            success("Package %s copied to %s" % (os.path.basename(package_path), export_path))
        else:
            error("Unable to copy %s to %s" % (os.path.basename(package_path), export_path))

def export_rpms(repo_path, package_paths, export_path=None):
    """
    Export all generated RPMS in custom folder
    @type repo_path: String
    @param repo_path: base path of the packages
    @type package_path: list
    @param package_path: array of all module paths
    @type export_path: string
    @param export_path: the destination path. if None, it will be exported in ./Archipel_RPMS
    """
    if not export_path:
        export_path = os.path.join(repo_path, "Archipel_RPMS")
    if not os.path.isdir(export_path):
        os.makedirs(export_path)
    if not os.path.exists(os.path.join(export_path, "RPMS")):
        os.makedirs(os.path.join(export_path, "RPMS"))
    if not os.path.exists(os.path.join(export_path, "SRPMS")):
        os.makedirs(os.path.join(export_path, "SRPMS"))

    for package_path in package_paths:
        if os.system("cp -a %s/dist/*noarch.rpm %s/RPMS" % (package_path, export_path)) == 0:
            success("Package %s copied to %s/RPMS" % (os.path.basename(package_path), export_path))
        else:
            error("Unable to copy %s to %s/RPMS" % (os.path.basename(package_path), export_path))
        if os.system("cp -a %s/dist/*src.rpm %s/SRPMS" % (package_path, export_path)) == 0:
            success("Package %s copied to %s/SRPMS" % (os.path.basename(package_path), export_path))
        else:
            error("Unable to copy %s to %s/SRPMS" % (os.path.basename(package_path), export_path))


def uninstall():
    """
    Completely uninstall Archipel central agent files.
    Data files are kept.
    """
    try:
        import site
    except:
        error("You don't have the python site module. Sorry, you need to uninstall Archipel manually.")
    for site_package_path in site.getsitepackages():
        msg("Cleaning archipel from %s" % site_package_path)
        os.system("rm -rf %s/archipel*" % site_package_path)
        success("Folder %s cleared" % site_package_path)
        local_easy_install_pth = os.path.join(site_package_path, "easy-install.pth")
        if os.path.exists(local_easy_install_pth):
            msg("Trimming %s/easy-install.pth file" % site_package_path)
            os.system("sed '/archipel*./d' %s -i" % local_easy_install_pth)
            success("Archipel entries removed from %s" % local_easy_install_pth)

    msg("Cleaning binary scripts")
    for script_name in ["adminaccounts", "commandsbytag", "centralagent", "initinstall", "tagnode", \
                        "updatedomain", "vmrequestnode", "command", "importvirtualmachine",\
                        "rolesnode", "testxmppserver", "vmparkingnode"]:
        if os.system("which archipel-%s &> /dev/null" % script_name) == 0:
            os.system("rm -f `which archipel-%s`" % script_name)
            success("archipel-%s script has been removed" % script_name)

def build_apscheduler_rpm(export_path):
    """
    Build the APScheduler RPM
    @type export_path: String
    @param export_path: the path where to copy the RPM after build
    """
    apscheduler_url = "http://pypi.python.org/packages/source/A/APScheduler/APScheduler-2.0.2.tar.gz"
    msg("Downloading the APScheduler source from pypi")
    if os.system("cd /tmp && wget %s" % apscheduler_url):
        error("Unable to download APScheduler source from %s" %apscheduler_url)
    success("APScheduler source downloaded")
    msg("Untaring the source")
    if os.system("cd /tmp && tar -xzf APScheduler-2.0.2.tar.gz"):
        error("Unable to untar the source tar")
    success("Source untared")
    msg("Building the RPM")
    if os.system("cd /tmp/APScheduler-2.0.2 && python setup.py bdist_rpm"):
        error("Unable to build the RPM")
    success("RPM Built")
    msg("Copying RPMS to %s" % export_path)
    if os.system("cp /tmp/APScheduler-2.0.2/dist/*rpm '%s'" % export_path):
        error("Unable to copy the RPM to repo path %s" % export_path)
    success("APScheduler RPM ready")


### Main function

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("-b", "--buildeggs",
                        dest="build_eggs",
                        help="Generate the EGGS",
                        action="store_true",
                        default=False)
    parser.add_argument("-B", "--buildprms",
                        dest="build_rpms",
                        help="Generate the RPMS",
                        action="store_true",
                        default=False)
    parser.add_argument("-u", "--upload",
                        dest="pypi_upload",
                        help="Upload to pypi using your .pypirc",
                        action="store_true",
                        default=False)
    parser.add_argument("-r", "--register",
                        dest="pypi_register",
                        help="register to pypi using your .pypirc",
                        action="store_true",
                        default=False)
    parser.add_argument("-c", "--clean",
                        dest="clean",
                        help="Clean build",
                        action="store_true",
                        default=False)
    parser.add_argument("-d", "--devinstall",
                        dest="devinstall",
                        help="Install Archipel Central Agent using the developer mode",
                        action="store_true",
                        default=False)
    parser.add_argument("-e", "--export",
                        dest="export",
                        help="Export all RPMS to ./Archipel_RPMS",
                        action="store_true",
                        default=False)
    parser.add_argument("-U", "--uninstall",
                        dest="uninstall",
                        help="Remove all Archipel's binary files. Data files (info, conf) remains safe",
                        action="store_true",
                        default=False)

    options = parser.parse_args()

    os.chdir(PATH)
    packages_paths = parse_repo(PATH)

    if options.build_eggs:
        if options.pypi_upload:
            msg("Generating python EGGS and upload them to PyPi")
        else:
            msg("Generating python EGGS")
        generate_eggs(packages_paths, upload=options.pypi_upload)
        if options.export:
            msg("Exporting the EGGS in Archipel_EGGS")
            export_eggs(PATH, packages_paths)
        msg("All action performed")

    elif options.build_rpms:
        msg("Generating RPMS")
        generate_rpms(packages_paths)
        if options.export:
            msg("Exporting the RPMS in %s")
            export_rpms(PATH, packages_paths)
        msg("All action performed")

    elif options.pypi_register:
        msg("Registering the EGGS on PyPi")
        pypi_register(packages_paths)
        msg("All action performed")

    elif options.devinstall:
        msg("Performing the developer installation")
        install_dev(packages_paths)
        msg("All action performed")

    elif options.clean:
        msg("Cleaning build")
        install_clean(PATH)
        msg("All action performed")

    elif options.uninstall:
        msg("Uninstalling Archipel")
        uninstall()
        msg("All action performed")

